/***********************************************************
     Starter code for Assignment 3

     This code was originally written by Jack Wang for
		    CSC418, SPRING 2005

		This file contains the interface and 
		datastructures of the raytracer.  
		Simple traversal and addition code to 
		the datastructures are given to you.

***********************************************************/

#include "Utils/util.h"
#include "Shapes/scene_object.h"
#include "Lights/light_source.h"
#include "ISceneNavigator.h"
#include "Shapes/InvisibleShape.h"

// Linked list containing light sources in the scene.
struct LightListNode {
	LightListNode() : light(NULL), next(NULL) {}
	LightListNode( LightSource* light, LightListNode* next = NULL ) : 
		light(light), next(next) {}
	~LightListNode() { 
		if (!light) delete light; 
	}
	LightSource* light;
	LightListNode* next;
};

// The scene graph, containing objects in the scene.
struct SceneDagNode {
	SceneDagNode(SceneObject* object = new InvisibleShape()) :
		obj(object), next(NULL), parent(NULL), child(NULL) {
	}	

	~SceneDagNode() {
		if (!obj) delete obj;		
	}

	// Pointer to geometry primitive, used for intersection.
	SceneObject* obj;
	
	// Internal structure of the tree, you shouldn't have to worry 
	// about them.
	SceneDagNode* next;
	SceneDagNode* parent;
	SceneDagNode* child;
};

class MotionBlur;

class Raytracer : public ISceneNavigator
{
public:
	Raytracer();
	~Raytracer();

	// Renders an image fileName with width and height and a camera
	// positioned at eye, with view vector view, up vector up, and 
	// field of view fov.
	void render( int width, int height, Point3D eye, Vector3D view, 
			Vector3D up, double fov, char* fileName );

	// Add an object into the scene, with material mat.  The function
	// returns a handle to the object node you just added, use the 
	// handle to apply transformations to the object.
	SceneDagNode* addObject( SceneObject* obj) {
		return addObject(_root, obj);
	}
	
	// Add an object into the scene with a specific parent node, 
	// don't worry about this unless you want to do hierarchical 
	// modeling.  You could create nodes with NULL obj and mat, 
	// in which case they just represent transformations.  
	SceneDagNode* addObject( SceneDagNode* parent, SceneObject* obj);

	// Add a light source.
	LightListNode* addLightSource( LightSource* light, bool enableSpecular);	
    
    // Sets the motion blur
    void setMotionBlur(MotionBlur* motionBlur);
    
    // Exposes navigation via ISceneNavigator interface
    virtual void navigateScene(Ray3D& ray);
	
    // Constructs a view to world transformation matrix based on the
	// camera parameters.
	static Matrix4x4 initInvViewMatrix( Point3D eye, Vector3D view, Vector3D up );
    
private:
	// Allocates and initializes the pixel buffer for rendering, you
	// could add an interesting background to your scene by modifying 
	// this function.
	void initPixelBuffer();

	// Saves the pixel buffer to a file and deletes the buffer.
	void flushPixelBuffer(char *file_name);

	// Return the colour of the ray after intersection and shading, call 
	// this function recursively for reflection and refraction.  
	Colour shadeRay( Ray3D& ray );
    
    Colour shadeShadow(Ray3D& ray, bool isInShadow);
    
    // Recursively applies shading upto MAX_LEVEL
    Colour shadeRay(Ray3D& ray, int level);

	// Traversal code for the scene graph, the ray is transformed into 
	// the object space of each node where intersection is performed.
	void traverseScene( SceneDagNode* node, Ray3D& ray );

	// After intersection, calculate the colour of the ray by shading it
	// with all light sources in the scene.
	void computeShading( Ray3D& ray );
    
    // Determines whether a specified point is in shadow
    bool isInShadow(Point3D point);
    
    // Returns global ambient color
    Colour getAmbientColor(const Intersection& intersection, bool inShadow);
    
    // Returns the reflection colour
    Colour getReflectionColor(Ray3D& incidentRay, int level);
    
    // Returns the refraction colour
    Colour getRefractionColour(Ray3D& incidentRay, int level);
    
    // Returns the gloss colour
    Colour getGlossColour(Ray3D& incidentRay, unsigned int glossFactor, int level);
	
	// Width and height of the viewport.
	int _scrWidth;
	int _scrHeight;

	// Light list and scene graph.
	LightListNode *_lightSource;
	SceneDagNode *_root;

	// Pixel buffer.
	unsigned char* _rbuffer;
	unsigned char* _gbuffer;
	unsigned char* _bbuffer;

	// Maintain global transformation matrices similar to OpenGL's matrix
	// stack.  These are used during scene traversal. 
	Matrix4x4 _modelToWorld;
	Matrix4x4 _worldToModel;
    
    const int MAX_LEVEL;
    
    MotionBlur* _pMotionBlur;
};
